﻿using System;
using System.Collections.Generic;
using BlobNet.Base.Collections;
using BlobNet.Base.DataProviders;
using BlobNet.Base.Enums;
using BlobNet.TestsCommon.UnitTests.Dummy;
using BlobNet.TestsCommon.Utils;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Impl;

namespace BlobNet.Base.Tests.UnitTests.StorageTests
{
    [TestFixture]
    [Category("Unit Tests")]
    public class PersistentStorageTests
    {
        [TestFixtureSetUp]
        public void StartFixture()
        {
            RhinoMocks.Logger = new TextWriterExpectationLogger(Console.Out);
        }
       
        [Test]
        public void Can_Retrieve_Array_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            var expected = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x =>
                x.RetrievePropertyValue<int[]>(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything))
                .Return(expected);

            Guid idEntity = Guid.NewGuid();
            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            var actual = target.GetStatusValue<int[]>(testProperty);

            dataProvider.AssertWasCalled(
                x => x.RetrievePropertyValue<int[]>(blobDomain.EntityId, idEntity, testProperty));
            Assert.AreEqual(actual, expected);
        }


        [Test]
        public void Can_Retrieve_BlobPart_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            string testProperty = "SampleProperty";
            Guid idEntity = Guid.NewGuid();

            var expected = new DummyBlobPart(blobDomain);

            dataProvider.Stub(
                x =>
                x.RetrievePropertyValue<Guid>(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Equal(idEntity),
                                              Arg<string>.Is.Equal(testProperty))).Return(expected.EntityId);

            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            var actual = target.GetStatusValue<DummyBlobPart>(testProperty);

            dataProvider.AssertWasCalled(x=>x.RetrievePropertyValue<Guid>(blobDomain.EntityId,target.EntityId,testProperty));

            Assert.IsTrue(actual.EntityId == expected.EntityId);
            Assert.IsTrue(actual.Domain.EntityId.ToString() == expected.BlobDomain.EntityId.ToString());
            Assert.IsTrue(actual.StatusStorage == StatusStorageType.Persistent);
        }

        [Test]
        public void Can_GetItems()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            Guid idEntity = Guid.NewGuid();

            var expected = new DummyBlobPart(blobDomain);

            dataProvider.Stub(
                x =>
                x.RetrieveItemsIds<DummyBlobPart>(Arg<Guid>.Is.Anything)).Return(new List<Guid>() {expected.EntityId});

            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            var actual = target.GetItems<DummyBlobPart>();

            dataProvider.AssertWasCalled(x => x.RetrieveItemsIds<DummyBlobPart>(blobDomain.EntityId));
            
            Assert.IsTrue(actual.Count==1);
            Assert.IsTrue(actual[0].EntityId==expected.EntityId);
        }

        [Test]
        [Ignore]
        public void Can_Get_HasPropertyValue()
        {
            Assert.Inconclusive("Check for every type");
        }

        [Test]
        [Ignore]
        public void Can_Retrieve_IEnumerable_PropertyValue()
        {
            Assert.Inconclusive("Check for every type");
        }

        //[Test]
        //public void Cant_Retrieve_NonSupported_Object_PropertyValue()
        //{
        //    BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
        //    var dataProvider = MockRepository.GenerateMock<IShDataProvider>();
        //    string testProperty = "Can_Retrieve_Object_PropertyValue";
        //    var expected = new DummyObject {Property1 = "AA"};

        //    Guid idEntity = Guid.NewGuid();
        //    Guid objId = Guid.NewGuid();
        //    dataProvider.Stub(x => x.RetrieveObject(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Equal(objId))).Return(expected);

        //    var target = new DummyDataProviderStorage(idEntity, blobDomain, dataProvider);

        //    target.Cache[testProperty] = objId;

        //    Assert.Throws(typeof(NotSupportedException),()=>target.GetStatusValue<DummyObject>(testProperty));

        //    dataProvider.AssertWasNotCalled(x => x.RetrieveObject(blobDomain.EntityId, objId));

        //}

        [Test]
        public void Can_Retrieve_RefType_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            string expected = "Test value";
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x =>
                x.RetrievePropertyValue<string>(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything)).
                Return(expected);

            Guid idEntity = Guid.NewGuid();
            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            var actual = target.GetStatusValue<string>(testProperty);

            dataProvider.AssertWasCalled(
                x => x.RetrievePropertyValue<string>(blobDomain.EntityId, idEntity, testProperty));
            Assert.IsTrue(actual == expected);
        }

        [Test]
        public void Can_Retrieve_ValueType_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            int expected = int.MaxValue;
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x => x.RetrievePropertyValue<int>(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything))
                .Return(expected);

            Guid idEntity = Guid.NewGuid();
            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            var actual = target.GetStatusValue<int>(testProperty);

            dataProvider.AssertWasCalled(x => x.RetrievePropertyValue<int>(blobDomain.EntityId, idEntity, testProperty));
            Assert.IsTrue(actual == expected);
        }

        [Test]
        public void Can_Save_ArrayOfValueTypes_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            int[] expected = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x =>
                x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                   Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<int[]>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(Guid.NewGuid(), blobDomain, dataProvider);

            target.SetStatusValue(testProperty, expected);

            dataProvider.AssertWasCalled(
                x =>
                x.AddPropertyValue<object>(blobDomain.EntityId, target.EntityId, testProperty, PropertyValueType.Value,typeof(int[]).FullName,
                                           expected));
        }

        [Test]
        public void Can_Save_BlobPart_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            Guid idEntity = Guid.NewGuid();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            string testProperty = "SampleProperty";

            var value = new DummyBlobPart(blobDomain);
            Guid expected = value.EntityId;
            dataProvider.Stub(
                x =>
                x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                   Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<int>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            target.SetStatusValue(testProperty, value);

            dataProvider.AssertWasCalled(
                x =>
                x.AddPropertyValue<object>(blobDomain.EntityId, idEntity, testProperty, PropertyValueType.Entity,typeof(DummyBlobPart).FullName,
                                           expected));
        }

        //[Test]
        //public void Cant_Save_Object_PropertyValue()
        //{
        //    BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
        //    Guid idEntity = Guid.NewGuid();
        //    var value = new DummyObject {Property1 = "test"};
        //    string testProperty = "SampleProperty";
        //    Guid expected = Guid.NewGuid();
        //    var dataProvider = MockRepository.GenerateMock<IShDataProvider>();
        //    dataProvider.Stub(x => x.AddObject(Arg<Guid>.Is.Anything, Arg<object>.Is.Equal(value))).Return(expected);
        //    dataProvider.Stub(
        //        x =>
        //        x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
        //                           Arg<PropertyValueType>.Is.Anything, Arg<int>.Is.Anything));

        //    var target = new DummyDataProviderStorage(idEntity, blobDomain, dataProvider);

        //    Assert.Throws(typeof(NotSupportedException),()=>target.SetStatusValue(testProperty, value));

        //    dataProvider.AssertWasNotCalled(x => x.AddObject(blobDomain.EntityId, value));
        //    dataProvider.AssertWasNotCalled(
        //        x =>
        //        x.AddPropertyValue(blobDomain.EntityId, idEntity, testProperty, PropertyValueType.WrappedObject, expected));
        //}

        [Test]
        public void Can_Save_RefType_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();

            string value = "String sample";
            string testProperty = "SampleProperty";
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            dataProvider.Stub(
                x =>
                x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                   Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(Guid.NewGuid(), blobDomain, dataProvider);

            target.SetStatusValue(testProperty, value);

            dataProvider.AssertWasCalled(
                x =>
                x.AddPropertyValue<object>(blobDomain.EntityId, target.EntityId, testProperty, PropertyValueType.Value, typeof(string).FullName,
                                           value));
        }

        [Test]
        public void Can_Save_BlobList_PropertyValue()
        {
            Guid idEntity = Guid.NewGuid();
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            dataProvider.Stub(
                x =>
                x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                   Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<object>.Is.Anything));
            var expected = new BlobList<DummyBlobPart>(blobDomain) {new DummyBlobPart(blobDomain)};

            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);
            Assert.DoesNotThrow(() => target.SetStatusValue("ListProperty", expected));
            
            dataProvider.AssertWasCalled(
                x =>
                x.AddPropertyValue<object>(blobDomain.EntityId, idEntity, "ListProperty", PropertyValueType.Entity,expected.GetType().FullName,
                                           expected.EntityId));
        }


        [Test]
        public void Can_Save_ValueType_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            int value = int.MaxValue;
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x =>
                x.AddPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                   Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<int>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(Guid.NewGuid(), blobDomain, dataProvider);

            target.SetStatusValue(testProperty, value);

            dataProvider.AssertWasCalled(
                x =>
                x.AddPropertyValue<object>(blobDomain.EntityId, target.EntityId, testProperty, PropertyValueType.Value, value.GetType().FullName,
                                           value));
        }


        [Test]
        public void Can_Update_BlobPart_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            Guid idEntity = Guid.NewGuid();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            string testProperty = "SampleProperty";

            var value = new DummyBlobPart(blobDomain);
            Guid expected = value.EntityId;
            dataProvider.Stub(
                x =>
                x.HasPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Equal(testProperty))).
                Return(true);
            dataProvider.Stub(
                x =>
                x.UpdatePropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                      Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<int>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            value = new DummyBlobPart(blobDomain);
            target.SetStatusValue(testProperty, value);

            dataProvider.AssertWasCalled(
                x =>
                x.UpdatePropertyValue<object>(blobDomain.EntityId, idEntity, testProperty, PropertyValueType.Entity,value.GetType().FullName,
                                              value.EntityId));

            //Assert.IsTrue(target.Cache.ContainsKey(testProperty) && (Guid) target.Cache[testProperty] == value.EntityId);
        }

        [Test]
        [Ignore]
        public void Can_Update_IEnumerable_PropertyValue()
        {
            Assert.Inconclusive();
        }

        [Test]
        public void Can_Update_ValueType_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            int value = int.MaxValue;
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x =>
                x.HasPropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Equal(testProperty))).
                Return(true);
            dataProvider.Stub(
                x =>
                x.UpdatePropertyValue(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything,
                                      Arg<PropertyValueType>.Is.Anything, Arg<string>.Is.Anything, Arg<int>.Is.Anything));

            var target = new PersistentStorageManagerTestHelper(Guid.NewGuid(), blobDomain, dataProvider);
            //target.Cache.Add(testProperty, value);

            target.SetStatusValue(testProperty, value);

            dataProvider.AssertWasCalled(
                x =>
                x.UpdatePropertyValue<object>(blobDomain.EntityId, target.EntityId, testProperty, PropertyValueType.Value,value.GetType().ToString(),
                                              value));
        }

        [Test]
        public void Cant_Save_ArrayOfNotValueTypes_PropertyValue()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            var expected = new[] {new object(), new object()};
            string testProperty = "SampleProperty";

            var target = new PersistentStorageManagerTestHelper(Guid.NewGuid(), blobDomain, dataProvider);

            Assert.Throws(typeof (NotSupportedException), () => target.SetStatusValue(testProperty, expected));
        }

        [Test]
        public void Retrieve_NotExisting_PropertyValue_Raises_Exception()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var dataProvider = MockRepository.GenerateMock<IDataProvider>();
            string testProperty = "SampleProperty";
            dataProvider.Stub(
                x => x.RetrievePropertyValue<int>(Arg<Guid>.Is.Anything, Arg<Guid>.Is.Anything, Arg<string>.Is.Anything))
                .Throw(new KeyNotFoundException("Property not found"));

            Guid idEntity = Guid.NewGuid();
            var target = new PersistentStorageManagerTestHelper(idEntity, blobDomain, dataProvider);

            Assert.Throws(typeof(KeyNotFoundException), () => target.GetStatusValue<int>(testProperty));

            dataProvider.AssertWasCalled(x => x.RetrievePropertyValue<int>(blobDomain.EntityId, idEntity, testProperty));
        }

        [Test]
        public void Can_Store_Item()
        {
            throw new NotImplementedException();
        }

        [Test]
        public void Can_Update_Relation_When_1_1()
        {
            throw new NotImplementedException();
        }

        [Test]
        public void Can_Update_Relation_When_n_1()
        {
            throw new NotImplementedException();
        }

        [Test]
        public void Can_Update_Relation_When_1_n()
        {
            throw new NotImplementedException();
        }

        [Test]
        public void Can_Update_Relation_When_n_m()
        {
            throw new NotImplementedException();
        }

    }
}